#include <assert.h>
#include <monitor.h>
#include <proc.h>
#include <sem.h>
#include <stdio.h>

#define N 5                  /* 哲学家数目 */
#define LEFT (i - 1 + N) % N /* i的左邻号码 */
#define RIGHT (i + 1) % N    /* i的右邻号码 */
#define THINKING 0           /* 哲学家正在思考 */
#define HUNGRY 1             /* 哲学家想取得叉子 */
#define EATING 2             /* 哲学家正在吃面 */
#define TIMES 4              /* 吃4次饭 */
#define SLEEP_TIME 10

//-----------------philosopher problem using monitor ------------
/*PSEUDO CODE :philosopher problem using semaphore
system DINING_PHILOSOPHERS

VAR
me:    semaphore, initially 1;                    # for mutual exclusion
s[5]:  semaphore s[5], initially 0;               # for synchronization
pflag[5]: {THINK, HUNGRY, EAT}, initially THINK;  # philosopher flag

# As before, each philosopher is an endless cycle of thinking and eating.

procedure philosopher(i)
  {
    while TRUE do
     {
       THINKING;
       take_chopsticks(i);
       EATING;
       drop_chopsticks(i);
     }
  }

# The take_chopsticks procedure involves checking the status of neighboring
# philosophers and then declaring one's own intention to eat. This is a
two-phase # protocol; first declaring the status HUNGRY, then going on to EAT.

procedure take_chopsticks(i)
  {
    DOWN(me);               # critical section
    pflag[i] := HUNGRY;
    test[i];
    UP(me);                 # end critical section
    DOWN(s[i])              # Eat if enabled
   }

void test(i)                # Let phil[i] eat, if waiting
  {
    if ( pflag[i] == HUNGRY
      && pflag[i-1] != EAT
      && pflag[i+1] != EAT)
       then
        {
          pflag[i] := EAT;
          UP(s[i])
         }
    }


# Once a philosopher finishes eating, all that remains is to relinquish the
# resources---its two chopsticks---and thereby release waiting neighbors.

void drop_chopsticks(int i)
  {
    DOWN(me);                # critical section
    test(i-1);               # Let phil. on left eat if possible
    test(i+1);               # Let phil. on rght eat if possible
    UP(me);                  # up critical section
   }

*/
//---------- philosophers problem using semaphore ----------------------
int state_sema[N]; /* 记录每个人状态的数组 */
/* 信号量是一个特殊的整型变量 */
semaphore_t mutex; /* 临界区互斥 */
semaphore_t s[N];  /* 每个哲学家一个信号量 */

struct proc_struct *philosopher_proc_sema[N];

void phi_test_sema(int i) /* i：哲学家号码从0到N-1 */
{
  if (state_sema[i] == HUNGRY && state_sema[LEFT] != EATING &&
      state_sema[RIGHT] != EATING) {
    state_sema[i] = EATING;
    up(&s[i]);
  }
}

void phi_take_forks_sema(int i) /* i：哲学家号码从0到N-1 */
{
  down(&mutex);           /* 进入临界区 */
  state_sema[i] = HUNGRY; /* 记录下哲学家i饥饿的事实 */
  phi_test_sema(i);       /* 试图得到两只叉子 */
  up(&mutex);             /* 离开临界区 */
  down(&s[i]);            /* 如果得不到叉子就阻塞 */
}

void phi_put_forks_sema(int i) /* i：哲学家号码从0到N-1 */
{
  down(&mutex);             /* 进入临界区 */
  state_sema[i] = THINKING; /* 哲学家进餐结束 */
  phi_test_sema(LEFT);      /* 看一下左邻居现在是否能进餐 */
  phi_test_sema(RIGHT);     /* 看一下右邻居现在是否能进餐 */
  up(&mutex);               /* 离开临界区 */
}

int philosopher_using_semaphore(void *arg) /* i：哲学家号码，从0到N-1 */
{
  int i, iter = 0;
  i = (int)arg;
  cprintf("I am No.%d philosopher_sema\n", i);
  while (iter++ < TIMES) { /* 无限循环 */
    cprintf("Iter %d, No.%d philosopher_sema is thinking\n", iter,
            i); /* 哲学家正在思考 */
    do_sleep(SLEEP_TIME);
    phi_take_forks_sema(i);
    /* 需要两只叉子，或者阻塞 */
    cprintf("Iter %d, No.%d philosopher_sema is eating\n", iter, i); /* 进餐 */
    do_sleep(SLEEP_TIME);
    phi_put_forks_sema(i);
    /* 把两把叉子同时放回桌子 */
  }
  cprintf("No.%d philosopher_sema quit\n", i);
  return 0;
}

//-----------------philosopher problem using monitor ------------
/*PSEUDO CODE :philosopher problem using monitor
 * monitor dp
 * {
 *  enum {thinking, hungry, eating} state[5];
 *  condition self[5];
 *
 *  void pickup(int i) {
 *      state[i] = hungry;
 *      if ((state[(i+4)%5] != eating) && (state[(i+1)%5] != eating)) {
 *        state[i] = eating;
 *      else
 *         self[i].wait();
 *   }
 *
 *   void putdown(int i) {
 *      state[i] = thinking;
 *      if ((state[(i+4)%5] == hungry) && (state[(i+3)%5] != eating)) {
 *          state[(i+4)%5] = eating;
 *          self[(i+4)%5].signal();
 *      }
 *      if ((state[(i+1)%5] == hungry) && (state[(i+2)%5] != eating)) {
 *          state[(i+1)%5] = eating;
 *          self[(i+1)%5].signal();
 *      }
 *   }
 *
 *   void init() {
 *      for (int i = 0; i < 5; i++)
 *         state[i] = thinking;
 *   }
 * }
 */

struct proc_struct *philosopher_proc_condvar[N]; // N philosopher
int state_condvar[N];     // the philosopher's state: EATING, HUNGARY, THINKING
monitor_t mt, *mtp = &mt; // monitor

void phi_test_condvar(int i) {
  if (state_condvar[i] == HUNGRY && state_condvar[LEFT] != EATING &&
      state_condvar[RIGHT] != EATING) {
    cprintf("phi_test_condvar: state_condvar[%d] will eating\n", i);
    state_condvar[i] = EATING;
    cprintf("phi_test_condvar: signal self_cv[%d] \n", i);
    cond_signal(&mtp->cv[i]);
  }
}

void phi_take_forks_condvar(int i) {
  down(&(mtp->mutex));
  //--------into routine in monitor--------------
  // LAB7 EXERCISE1: YOUR CODE
  // I am hungry
  // try to get fork
  state_condvar[i] = HUNGRY;
  phi_test_condvar(i);
  while (state_condvar[i] != EATING) {
    cond_wait(&mtp->cv[i]);
  }
  //--------leave routine in monitor--------------
  if (mtp->next_count > 0)
    up(&(mtp->next));
  else
    up(&(mtp->mutex));
}

void phi_put_forks_condvar(int i) {
  down(&(mtp->mutex));
  //--------into routine in monitor--------------
  // LAB7 EXERCISE1: YOUR CODE
  // I ate over
  // test left and right neighbors
  state_condvar[i] = THINKING;
  phi_test_condvar(LEFT);
  phi_test_condvar(RIGHT);
  //--------leave routine in monitor--------------
  if (mtp->next_count > 0)
    up(&(mtp->next));
  else
    up(&(mtp->mutex));
}

//---------- philosophers using monitor (condition variable)
//----------------------
int philosopher_using_condvar(
    void *arg) { /* arg is the No. of philosopher 0~N-1*/

  int i, iter = 0;
  i = (int)arg;
  cprintf("I am No.%d philosopher_condvar\n", i);
  while (iter++ < TIMES) { /* iterate*/
    cprintf("Iter %d, No.%d philosopher_condvar is thinking\n", iter,
            i); /* thinking*/
    do_sleep(SLEEP_TIME);
    phi_take_forks_condvar(i);
    /* need two forks, maybe blocked */
    cprintf("Iter %d, No.%d philosopher_condvar is eating\n", iter,
            i); /* eating*/
    do_sleep(SLEEP_TIME);
    phi_put_forks_condvar(i);
    /* return two forks back*/
  }
  cprintf("No.%d philosopher_condvar quit\n", i);
  return 0;
}

void check_sync(void) {

  int i;

  // check semaphore
  sem_init(&mutex, 1);
  for (i = 0; i < N; i++) {
    sem_init(&s[i], 0);
    int pid = kernel_thread(philosopher_using_semaphore, (void *)i, 0);
    if (pid <= 0) {
      panic("create No.%d philosopher_using_semaphore failed.\n");
    }
    philosopher_proc_sema[i] = find_proc(pid);
    set_proc_name(philosopher_proc_sema[i], "philosopher_sema_proc");
  }

  // check condition variable
  monitor_init(&mt, N);
  for (i = 0; i < N; i++) {
    state_condvar[i] = THINKING;
    int pid = kernel_thread(philosopher_using_condvar, (void *)i, 0);
    if (pid <= 0) {
      panic("create No.%d philosopher_using_condvar failed.\n");
    }
    philosopher_proc_condvar[i] = find_proc(pid);
    set_proc_name(philosopher_proc_condvar[i], "philosopher_condvar_proc");
  }
}
